package tue.cse.advanceddatabases.project;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;

import org.apache.commons.httpclient.NameValuePair;
import org.dom4j.Element;

import tue.cse.advanceddatabases.project.xml.album.Album;
import tue.cse.advanceddatabases.project.xml.album.AlbumsParser;
import tue.cse.advanceddatabases.project.xml.artist.ArtistsParser;
import tue.cse.advanceddatabases.project.xml.genre.Genre;
import tue.cse.advanceddatabases.project.xml.genre.GenreParser;
import tue.cse.advanceddatabases.project.xml.track.Track;
import tue.cse.advanceddatabases.project.xml.track.TrackParser;

public class TrackEngineBean extends MusicEngineBean {
	private Logger logger = Logger.getLogger(TrackEngineBean.class.getName());
	private List<Track> tracks;

	private String trackToSearch;
	private Track _track = new Track();
	
	/*
	 * public properties for private properties
	 */
	
	public String getTrackToSearch() {
		return trackToSearch;
	}

	public void setTrackToSearch(String trackToSearch) {
		this.trackToSearch = trackToSearch;
	}
	
	public Track getDefaultTrack(){
	  	return _track;
	}
	
	/*
	 * Actions: public methods (query constructions)
	 */
	
	/** return tracks for "trackToSearch" **/
	public List<Track> getTracks(){
		return tracks;
	}
	
	/** get tracks by "albumKey" property **/
	public List<Track> getTracksByAlbumKey(){

		logger.info("Searching tracks by Album ID:");
		String albumId =  getAlbumKey();		
		
		String tracks = searchTracksByAlbumId(albumId);
		
		logger.info("Results found \n" + tracks);
		
		if (tracks != null){
			List<Element> tracksElement = removeExistDBElement(tracks);
			if (tracksElement!=null){
				logger.info("Tracks are contained in" + tracksElement);
				return TrackParser.parse(tracksElement);
			}
		}
		
		return null;
	}
	
	public List<Track> getRandomTracksByArtist(){
		logger.info("Searching random tracks by Tracks ID:");
		String artistId =  getTrack().getArtistId();		
		
		NameValuePair[] parameters = new NameValuePair[2];
		parameters[0] = new NameValuePair("artistId",artistId);
		parameters[1] = new NameValuePair("count","6");
		
		String tracks = executeQueryXQL("tracksByArtist.xql", parameters);
		
		logger.info("Results found \n" + tracks);
		
		if (tracks != null){
			Element tracksElement = stringToXml(tracks);
			if (tracksElement!=null){
				logger.info("Tracks are contained in" + tracksElement);
				return TrackParser.parse(tracksElement);
			}
		}
		
		return null;		
	}
	
	/** search tracks by "trackKey" parameter **/
	/** return current track searched by id **/
	public Track getTrack(){
		
		String track = searchByTrackId(getTrackKey());
		
		if (track != null){
			List<Element> tracksElement = removeExistDBElement(track);
			if (tracksElement != null){
				logger.info("Tracks are contained in" + tracksElement);
				List<Track> trackArray = TrackParser.parse(tracksElement);
				// Return always the first.
				return trackArray.get(0);
			}
		}
		
		return null;		
	}
	
	/** search tracks by "trackToSearch" property **/
	public void searchTracks(){
		tracks = null;
		logger.info("Searching tracks by Name");
		
		NameValuePair[] parameters = new NameValuePair[2];
		parameters[0] = new NameValuePair("name",trackToSearch);
		parameters[1] = new NameValuePair("order","rating-count");
		
		String result = executeQueryXQL("tracksQueryByName.xql",parameters);
		
		
		if (result != null){
			Element artistsElement = stringToXml(result);
			if (artistsElement!=null){
				logger.info("Tracks are contained in" + artistsElement);
				tracks = TrackParser.parse(artistsElement);
				return;
			}			
		}
		
		tracks = null;
	}
	
	/** get Years data  **/
	public List<SelectItem> getYearsList(){
	  	ArrayList selectItems = new ArrayList<SelectItem>();
	  	
	  	Date d = new Date();
	  	int year = d.getYear() + 1900;
	  	
	  	for (int i = year; i > 1900; i--){
	  	  	selectItems.add(new SelectItem(Integer.toString(i), Integer.toString(i)));
	  	}
	  	
	  	return selectItems; 
	}
	
	
	/** Access the manageTracks page with cleared values **/
	public String accessAddTrack(){
    	   	Clear();
    	   	_track.setArtistId(getArtistKey());
    	   	_track.setAlbumId(getAlbumKey());
    	   	return "manageTrack";
	}
	
	/** go to edit Track by "trackKey" parameter **/
	public String editTrack(){
    	    	this._track = getTrack();
    	    	setIsNew(false);
    	    	return "manageTrack";
	}
	
	/** remove Track by "trackKey" parameter **/
	public void removeTrack(){
    	    if (deleteTrack(getTrackKey())){
    	        searchTracks();
                status = "Item deleted.";
            }
            else{
                status = "Error while deleting!";
            }	
	}
	
	/** add genre **/
	public void addGenre(){
	  	logger.info("Inserting Genre");
	     
	  	String command = getInsertGenreForTrackCommand(getDefaultTrack().getId(), getDefaultTrack().getGenre());
		
	  	String query = updateBegin() + command + updateEnd();
	  	String result = executeQuery(query, "Tracks.xml");
		
	  	logger.info("Inserting result is:" + result);
	}
	
	/** remove Genre by "genreKey" parameter for track **/
	public void removeGenre(){
	        FacesContext facesContext = FacesContext.getCurrentInstance();
		String genre = (String) facesContext.getExternalContext().getRequestParameterMap().get("genreKey");
    	    	
		logger.info("Deleting Artist");
		     
		String command = getDeleteGenreCommand(getDefaultTrack().getId(), genre);
			
		String query = updateBegin() + command + updateEnd();
		String result = executeQuery(query, "Track.xml");
			
		logger.info("Deleting result is:" + result);
	}
	
	/** get all genres on track **/
	public List<Genre> getGenresByTrack(){
	  	logger.info(" Get Genres for Artist ID: " + getDefaultTrack().getId());
		
		String query = queryBegin() + getAllGenresCommand(getDefaultTrack().getId()) + queryEnd();
		String result = executeQuery(query,"Tracks.xml");
		
		if (result != null){
		  	List<Element> genresElement = removeExistDBElement(result);
			if (genresElement!= null){
				logger.info("Genres are contained in" + genresElement);
				return GenreParser.parse(genresElement);
			}
		}

		return null;
	}
	
	
	public void btnSave(){
	  
              if (getIsNew()){
                  if (insertTrack()){
                      status = "Item saved.";
                  }
                  else{
                      status = "Error while saving!";
                  }
              }
              else{
                  if (updateTrack()){
                      status = "Item saved.";
                  }
                  else{
                    	status = "Error while saving!";
                  }
              }
	}
	
	public void btnDelete(){
	  
	    if (deleteTrack(getDefaultTrack().getId())){
                SetupForm();
                status = "Item deleted.";
            }
            else{
                status = "Error while deleting!";
            }
	}
	
	public void btnNew(){
	     SetupForm();    
	}
	
	
	/** Clear values that are used in the bean **/
	private void Clear()
	{   
	    String artistId = _track.getArtistId();
	    String albumId = _track.getAlbumId();
	    
	    _track = new Track();
	    
	    if (artistId != null)
	      _track.setArtistId(artistId);
	    if (albumId != null)
	      _track.setAlbumId(albumId);
	    
	    setIsNew(true);
	}
	
	private void SetupForm()
	{
	    Clear();
	    status = "";
	}
	
	/*
	 * Private Query constructions
	 */
	
	private boolean insertTrack(){
		
		logger.info("Inserting Track");

		//TODO: generate artistId
		_track.setId(_track.getName());
		     
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String dateEntered = sdf.format(new Date());
		String command = getInsertTrackCommand(getDefaultTrack().getId(), getDefaultTrack().getArtistId(), getDefaultTrack().getAlbumId(), 
		    getDefaultTrack().getName(), getDefaultTrack().getSeconds(), getDefaultTrack().getYear(), getDefaultTrack().getDownloadUrl(), getDefaultTrack().getLaunchdate(), dateEntered);
		
		String query = updateBegin() + command + updateEnd();
		String result = executeQuery(query, "Tracks.xml");
		
		logger.info("Inserting result is:" + result);
		
		if (!("fail".equalsIgnoreCase(result))){
      	       	  setIsNew(false);	
      		  return true;
		}
		
		return false;
	}
	
	private boolean updateTrack(){
	  
		logger.info("Updating Track");

		String command = getUpdateTrackCommand(getDefaultTrack().getId(), getDefaultTrack().getArtistId(), getDefaultTrack().getAlbumId(), 
		    getDefaultTrack().getName(), getDefaultTrack().getSeconds(), getDefaultTrack().getYear(), getDefaultTrack().getDownloadUrl(), getDefaultTrack().getLaunchdate());
		
		String query = updateBegin() + command + updateEnd();
		String result = executeQuery(query,"Tracks.xml");
		
		logger.info("Updating result is:" + result);
		
		if (!("fail".equalsIgnoreCase(result))){
	       	  setIsNew(false);	
		  return true;
		}
		
		return false;
	}
	
	private boolean deleteTrack(String trackId){
		
		logger.info("Deleting Track"); 
		
		String command = getDeleteTrackCommand(trackId);
		
		String query = updateBegin() + command + updateEnd();
		String result = executeQuery(query, "Tracks.xml");
		
		logger.info("Deleting result is:" + result);
		
		if (!("fail".equalsIgnoreCase(result))){
	       	  setIsNew(false);	
		  return true;
		}
		
		return false;
	}
	
	private String searchByTrackId(String trackId){
		logger.info("Searching track by ID:");

		
		logger.info(" Search Track ID:" + trackId);

		String query = queryBegin() + trackIdQuery(trackId) + queryEnd();
		String result = executeQuery(query,"Tracks.xml");
		
		logger.info("track result:" + result);
		
		if (!("fail".equalsIgnoreCase(result)))
			return result;

		return null;
	}
	
	private String searchTracksByAlbumId(String albumId){
		logger.info("Searching Tracks by Album ID:" + albumId);

		String query = queryBegin() + tracksAlbumsIdQuery(albumId) + queryEnd();
		String result = executeQuery(query,"Tracks.xml");
		
		if (!("fail".equalsIgnoreCase(result)))
			return result;

		return null;		
	}
	
	/*
	 * Queries
	 */
	
	/** tracks query **/
	private String tracksQuery(){
		return "element { \"tracks\" }{ " +
			"for $track in //track " +
			"where contains($track/name/text(),\"" + trackToSearch + "\") " + 
			"return $track" +
			"}";
	}
	
	/** track id query **/
	private String trackIdQuery(String trackId){
		return "element { \"tracks\" }{ " +
			"for $track in //track " +
			"where $track/@id =\"" + trackId + "\" " + 
			"return $track" +
			"}";
	}
	
	/** tracks by album-id query **/
	private String tracksAlbumsIdQuery(String albumId){
		return "element { \"tracks\" }{ " +
			"for $track in //track " +
			"where $track/album_id/text() = \"" + albumId + "\" " + 
			"return $track" +
			"}";
	}
	

	/** insert track command **/
	private String getInsertTrackCommand(String trackId, String artistId, String albumId, 
	    	String name, String url, String launchDate, String year, String seconds, String dateEntered){
		return "<xu:append select=\"/tracks\">" + 
				"<xu:element name=\"track\">" +
				"<xu:attribute name=\"id\">" + trackId + "</xu:attribute>" +
				"<artist_id>" + artistId + "</artist_id>" +
				"<album_id>>" + albumId + "</album_id>>" +
				"<name>" + name + "</name>" +
				"<url>" + url + "</url>" +
				"<launchdate>" + launchDate + "</launchdate>" +
				"<year>" + year + "</year>" +
				"<seconds>" + seconds + "</seconds>" +
				"<genres></genres>" +
				"<rating>0</rating>" +
				"<rating-count>0</rating-count>" +
				"<comment-count>0</comment-count>" +
				"<date_created>" + dateEntered + "</date_created>" +
				"</xu:element></xu:append>";
	}
	
	/** update track command **/
	private String getUpdateTrackCommand(String trackId, String artistId, String albumId, 
	    	String name, String seconds, String year, String url, String launchDate){
		return "<xu:update select=\"/tracks/track[@id=\'" + trackId + "\"']/artist_id\">" +
			    artistId +
		       "</xu:update>" +
		       "<xu:update select=\"/tracks/track[@id=\'" + trackId + "\']/album_id\">" +
		            albumId +
		       "</xu:update>" +
		       "<xu:update select=\"/tracks/track[@id=\'" + trackId + "\']/name\">" +
		            name +
		       "</xu:update>" +
		       "<xu:update select=\"/tracks/track[@id=\'" + trackId + "\']/url\">" +
		            url +
		       "</xu:update>" +
		       "<xu:update select=\"/tracks/track[@id=\'" + trackId + "\']/launchdate\">" +
		            launchDate +
		       "</xu:update>" +
		       "<xu:update select=\"/tracks/track[@id=\'" + trackId + "\']/year\">" +
		            year +
		       "</xu:update>" +
		       "<xu:update select=\"/tracks/track[@id=\'" + trackId + "\']/seconds\">" +
		            seconds +
		       "</xu:update>";
	}
	
	/** delete track (and comments for that track) command **/
	private String getDeleteTrackCommand(String trackId){
		return "<xu:remove select=\"/tracks/track[@id = \'" + trackId + "\']\" />" +
		       "<xu:remove select=\"/comments/comment[music_id = \'" + trackId + "\']\" />";
	}	
	
	/** insert genre for track command **/
	private String getInsertGenreForTrackCommand(String trackId, String genre){
	  	return "<xu:append select=\"/tracks/track[@id=\'" + trackId + "\']/genres\">" + 
	  		"<xu:element name=\"genre\">" + genre +
	  		"</xu:element></xu:append>";
	}
	
	/** delete genre for track command **/
	private String getDeleteGenreCommand(String trackId, String genre){
		return "<xu:remove select=\"/tracks/track[@id = \'" + trackId + "\']/genres/genre[text() = \'" + genre + "\']\" />";
	}	
	
	/** get all track genres command **/
	private String getAllGenresCommand(String trackId){
	  	return "element { \"tracks\" }{ " +
	  		"for $track in //track " +
	  		"where $track/@id =\"" + trackId + "\" " +
	  		"return for $genre in $track/genres/genre " + 
	  		        "return $genre" +
	  		"}";
	}
}
